
--[[
test
--]]
assert(LibStub, "AdtSet-1.0 requires LibStub")

local Set, oldminor = LibStub:NewLibrary("AdtSet-1.0", 0)
if not Set then return end
oldminor = oldminor or 0

local kind=function(x)
	local t=type(x)
	if t=="nil" then return "nil" end
	if t~="table" then return "other" end
	local mt=getmetatable(x)
	if mt~=nil then return "other" end
	if mt==set_mt then return "Set" end
	return "table"
end 
local wk={__mode="k"}
local func
local sets=setmetatable({},wk)
local setSizes=setmetatable({},wk)

local set_mt={
	__call=function(t)
		return setSizes[t] 
	end,
	__newIndex=function(self,k,v)
		if v==nil then
			rawset(self,k,nil)
			setSizes[self]=setSizes[self]-1
		else
			rawset(self,k,true)
			setSizes[self]=setSizes[self]+1
		end
	end,
	__sub=function(op1,op2)
		local t1=kind(op1)	
		local t2=kind(op2)
		if func["difference"][t1][t2] then
			return func["diffrence"][t1][t2](op1,op2)
		end
		return nil
	end,
	__add=function(op1,op2)
		local t1=kind(op1)
		local t2=kind(op2)
		if func["union"][t1][t2] then
			return func["union"][t1][t2](op1,op2)
		end
		return nil
	end,
	__mul=function(op1,op2)
		local t1=kind(op1)
		local t2=kind(op2)
		if func["intersection"][t1][t2] then
			return func["intersection"][t1][t2](op1,op2)
		end
		return nil
	end,
	__div=function(op1,op2)
		local t1=kind(op1)
		local t2=kind(op2)
		if func["rev_intersection"][t1][t2] then
			return func["rev_intersection"][t1][t2](op1,op2)
		end
		return nil
	end,
	__eq=function(op1,op2)
		if setSizes[op1]~=setSizes[op2] then return false end
		for k,_ in pairs(op1) do
			if not op2[k] then return false
		end
		return true
	end		
}

--[[
	constructor Set()
--]]
local function new_set(...)
	local vset=setmetable({},set_mt)
	local c=select('#',...)
	local rc=c
	for i=1,c do
		local v= select(i,...)
		if v==nil then
			rc=rc-1
		elseif vset[v]~=nil then
			-- it already is in the set
			rc=rc-1
		else
			rawset(vset,v,true)
		end
	end
	setSizes[vset]=rc
	return vset
end

--[[
	Set functions
]]
Set.Size=function(set)
	return setSizes[set]
end
Set.Add=function(set,...)
	assert(getmetatable(set)==set_mt,"Can only add to a Set")
	local c=select("#",...)
	for i=1,c do
		local v=select(i,...)
		if v~=nil then
			set[v]=true
		end
	end
	return set
end
Set.Remove=function(set,...)
	assert(getmetatable(set)==set_mt,"Can only remove from a Set")
	local c=select("#",...)
	for i=1,c do
		local v=select(i,...)
		if v~=nil and set[v]~=nil then
			set[v]=nil
		end
	end
	return set
end
Set.HasAll=function (set,...)
	assert(getmetatable(set)==set_mt,"Can only check a Set for the existence of elements")
	local c=select("#",...)
	for i=1,c do
		local v=select(i,...)
		if v~=nil and set[v]==nil then return false end -- nil is the empty set and that is always in a set
	end
	return true
end
Set.HasOne=function (set,...)
	assert(getmetatable(set)==set_mt,"Can only check a Set for the existence of elements")
	local c=select("#",...)
	for i=1,c do
		local v=select(i,...)
		if v==nil then return true end -- nil is the empty set, and the empty set is always in a set
		if set[v]~=nil then return true end
	end
	return false
end
Set.MissAll=function(set,...)
	return not Set.HasOne(set,...)
end
Set.MissOne=function(set,...)
	return not Set.HasAll(set,...)		
end
setmetatable(Set,{__call=new_set,__metatable="class AdtSet-1.0"})

--[[
	set operations
--]]
func={
	union={
		table={
			Set=function(op1,op2)
				local vset=Set()
				for _,v in pairs(op1) do vset[v]=true end
				for k,_ in pairs(op2) do	 vset[k]=true end
				return vset
			}
		},
		Set={
			Set=function(op1,op2)
				local vset=Set()
				for k,_ in pairs(op1) do	 vset[k]=true end
				for k,_ in pairs(op2) do vset[k]=true end
				return vset
			end,
			table=function(op1,op2)
				local vset=Set()
				for k,_ in pairs(op1) do	 vset[k]=true end
				for _,v in pairs(op1) do vset[v]=true end
				return vset
			end,
			other=function(op1,op2)
				local vset=Set()
				for k,_ in pairs(op1) do vset[k]=true end
				vset[op2]=true
				return vset
			end,
			["nil"]=function(op1,op2)
				local vset=Set()
				for k,_ in pairs(op1) do vset[k]=true end
				return vset
			end
		},
		other={
			Set=function(op1,op2)
				local vset=Set()
				for k,_ in pairs(op2) do vset[k]=true end
				vset[op1]=true
				return vset
			end	
		},
		["nil"]={
			Set=function(op1,op2)
				local vset=Set()
				for k,_ in pairs(op2) do vset[k]=true end
				return vset
			end
		}
	},
	difference={ -- op1-op2
		table={
			Set=function(op1,op2)
				local vset=Set()+op1
				for k,_ in pairs(op2) do
					if vset[k] then vset[k]=nil end
				end
				return vset
			end
		},
		Set={
			table=function(op1,op2)
				local vset=Set()+op1
				for _,v in pairs(op2) do
					if vset[v] then vset[v]=nil end
				end
				return vset
			end,
			other=function(op1,op2)
				local vset=Set()
				for k,_ in pairs(op1) do if k~=op2 then vset[k]=true end end
				return vset
			end,
			["nil"]=function(op1,op2)
				local vset=Set()
				for k,_ in op1 do vset[k]=true end
				return vset
			end
		},
		other={
			Set=function(op1,op2)
				local vset=Set()
				if op2[op1] then return vset end
				vset[op1]=true
				return vset
			end
		},
		["nil"]={
			Set=function(op1,op2)
				return Set()
			end
		}
	},
	intersection={
		table={
			Set=function(op1,op2)
				local vset=Set()
				for _,v in pairs(op1) do
					if op2[v] then vset[v]=true end
				end
				return vset
			end
		},
		Set={
			table=function(op1,op2)
				local vset=Set()
				for _,v in pairs(op2) do
					if op1[v] then vset[v]=true end
				end
				return vset
			end,
			other=function(op1,op2)
				local vset=Set()
				if not op1[op2] then return vset end
				vset[op2]=true
				return vset
			end
		},
		other={
			Set=function(op1,op2)
				local vset=Set()
				if not op2[op1] then return vset end
				vset[op1]=true
				return vset
			end
		}
	},
	rev_intersection={
		table={
			Set=function(op1,op2)
				local vset=Set()+op1
				for k,_ in pairs(op2) do
					if vset[k] then
						vset[k]=nil
					else
						vset[k]=true
					end
				end
			end
		},
		Set={
			table=function(op1,op2)
				local vset=Set()+op2
				for k,_ in pairs(op1) do
					if vset[k] then
						vset[k]=nil
					else
						vset[k]=true
					end
				end
				return vset
			end,
			other=function(op1,op2)
				local vset=Set()
				for k,_ in pairs(op1) do vset[k]=true end end
				if vset[op2] then
					vset[op2]=nil
				else
					vset[op2]=true
				end
				return vset
			end
		},
		other={
			Set=function(op1,op2)
				local vset=Set()
				for k,_ in pairs(op2) do vset[k]=true end end
				if vset[op1] then
					vset[op1]=nil
				else
					vset[op1]=true
				end
				return vset
			end
		}
	}				
}